import mido
import objectStateConstants
import numpy

# import ASTF_class

debug = objectStateConstants.ObjectStateConstant()  # 项目数据加载


def loadMid(InMid, IN_ASTF):
    midi = mido.MidiFile(InMid)
    midiBPM_ergodic_algorithm(midi)

    tracks = []
    tempo = 0

    for i, track in enumerate(midi.tracks):
        ticks = 0  # 绝对tick数
        instrumentID = -1
        singleTrack = []

        for msg in track:
            debug.dp(msg)

            ticks += msg.time
            MC_tick = round_up(ticks * tempo) / (midi.ticks_per_beat * 50000)
            # 这个时间算法由Dislink Sforza提供
            # 原代码归属于musicreater的pkgver包

            if msg.is_meta:
                if msg.type == "copyright":
                    IN_ASTF.detailInformation["information"]["scoreMetaMessage"]["copyright"] = msg.text
                    IN_ASTF.detailInformation["information"]["midMetaMessage"]["midiCopyright"] = msg.text
                if msg.type == 'set_tempo':
                    tempo = msg.tempo
                    bpm_by_MetaMessage_Set_tempo(tempo)
                if msg.type == "time_signature":
                    pass
                if msg.type == "key_signature":
                    key = [msg.key, msg.time]
                if msg.type == "end_of_track":
                    pass
            else:
                if msg.type == 'program_change':
                    instrumentID = msg.program
                if msg.type == 'note_on' and msg.velocity != 0:
                    pass
                if msg.type == "note_off":
                    pass
            singleTrack.append(msg)

        if instrumentID == -1:
            trackInfo = {"MidiControlTrack": singleTrack}
        else:
            trackInfo = {"MidiNotesTrack": singleTrack}

        tracks.append(trackInfo)


def mt2gt(mt, tpb_a, bpm_a):
    return round(mt / tpb_a / bpm_a * 60)


def midiBPM_ergodic_algorithm(mid: mido.MidiFile):
    """
    一个测试mid bpm 的函数，采用遍历获取，在正常每小节都有音符的mid中效果较好，仅供参考。
    A function that's used to test and get midi's bpm.
    Using traversal acquisition, the effect is better in the mid with notes in each normal section,
    which is for reference only.
    :param mid: 一个mido的mid文件类
    A class of mido.MidiFile
    :return: bpm

    This algorithm is made by ©bgArray.
    算法版权归©诸葛亮与八卦阵所有。
    """
    long = mid.length
    tpb = mid.ticks_per_beat
    bpm = 20
    gotV = 0

    for track in mid.tracks:
        global_time = 0
        for msg in track:
            global_time += msg.time
            if msg.type == "note_on" and msg.velocity > 0:
                gotV = mt2gt(global_time, tpb, bpm)
    errorV = numpy.fabs(gotV - long)
    last_dic = {bpm: errorV}
    if last_dic.get(bpm) > errorV:
        last_dic = {bpm: errorV}
    bpm += 2

    while True:
        for track in mid.tracks:
            global_time = 0
            for msg in track:
                global_time += msg.time
                if msg.type == "note_on" and msg.velocity > 0:
                    gotV = mt2gt(global_time, tpb, bpm)
        errorV = numpy.fabs(gotV - long)
        try:
            if last_dic.get(bpm - 2) > errorV:
                last_dic = {bpm: errorV}
        except TypeError:
            pass
        bpm += 2
        if bpm >= 252:
            break
    debug.dp(list(last_dic.keys())[0])
    return list(last_dic.keys())[0]


def bpm_by_MetaMessage_Set_tempo(tmp: int):
    """
    midi文件tempo事件bpm算法。
    A function that's used to compute the bpm of a midiFile,
    which algorithm is made up of midiFile's tempo meta message.
    :param tmp:输入mid的metaMessage中速度tempo值
    input the tempo value which is in the tempo meta message.
    :return:bpm

    This algorithm is made by ©bgArray.
    算法版权归©诸葛亮与八卦阵所有。
    """
    second = tmp / 1000000
    bpm = delete_extra_zero(60 / second)
    debug.dp(bpm)
    return bpm


def delete_extra_zero(n: float):
    """
    ————————————————
    版权声明：本文为CSDN博主「XerCis」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
    原文链接：https://blog.csdn.net/lly1122334/article/details/108770141
    删除小数点后多余的0
    """
    n = '{:g}'.format(n)
    n = float(n) if '.' in n else int(n)  # 含小数点转float否则int
    return n


# 四舍五入
def round_up(num, power=0):
    """
    实现精确四舍五入，包含正、负小数多种场景
    原文链接：https://blog.csdn.net/wcl1800/article/details/115229069
    :param num: 需要四舍五入的小数
    :param power: 四舍五入位数，支持0-∞
    :return: 返回四舍五入后的结果
    """
    try:
        print(1 / 0)
    except ZeroDivisionError:
        digit = 10 ** power
        num2 = float(int(num * digit))
        # 处理正数，power不为0的情况
        if num >= 0 and power != 0:
            tag = num * digit - num2 + 1 / (digit * 10)
            if tag >= 0.5:
                return (num2 + 1) / digit
            else:
                return num2 / digit
        # 处理正数，power为0取整的情况
        elif num >= 0 and power == 0:
            tag = num * digit - int(num)
            if tag >= 0.5:
                return (num2 + 1) / digit
            else:
                return num2 / digit
        # 处理负数，power为0取整的情况
        elif power == 0 and num < 0:
            tag = num * digit - int(num)
            if tag <= -0.5:
                return (num2 - 1) / digit
            else:
                return num2 / digit
        # 处理负数，power不为0的情况
        else:
            tag = num * digit - num2 - 1 / (digit * 10)
            if tag <= -0.5:
                return (num2 - 1) / digit
            else:
                return num2 / digit


# ---------------
# 尘封的往事
# if __name__ == '__main__':
#     loadMid(r"C:\Users\lc\Documents\MuseScore3\乐谱\乐谱\同道殊途.mid")

# def compute(mid: mido.MidiFile):
#     answer = 60000000 / mid.ticks_per_beat
#     print(answer)
#     return answer

# try:
#     # print(tempo)
#     # print((ticks * tempo))
#     # print(((midi.ticks_per_beat * float(tempo)) * 50000))
#     mcTick = round(
#         (ticks * tempo) / (midi.ticks_per_beat * 50000)  # * float(tempo)
#     )
#     print(mcTick)
# except ZeroDivisionError:
#     mcTick = 0
# print(mcTick)
